﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
using namespace std;
//1.匿名类为临时变量，做返回值时返回值类型需为ls或const ls&
//2.当返回值类型为类类型（比如ls类类型），返回时会调用该类类型的拷贝构造
//	而调用拷贝构造的结果为常量，不能直接使用ls&接收，若想接收，则须使用const ls&类型
//3.当返回值为函数中新建的有名对象的引用时，由于在函数结束时会自动调用该局部变量的析构函数，
//	所以就算使用&类型接受该值，也不再准确（但需注意，匿名对象默认在表达式结束时析构，但通过const引用或右值引用可以延长其生命周期至引用作用域结束）
//4.const类型对象只能调用类中的this指针类型为const的函数，但非const对象既可以调用this指针为const的函数，也可以调用非const修饰的成员函数
//5.迭代器底层一般都是临时变量，不能直接使用引用类型接收
class ls
{
public:
	ls(int a = 0)
		:_a(a)
	{
	}
	void func() const//将const加在成员函数后，使this指针转换为const类型
	{
		cout << _a << endl;
	}
private:
	int _a;
};
ls test1()
{
	return ls();//当返回值类型为ls&时报错，匿名类为临时变量类型，即const修饰，返回值类型不能为ls&，应为ls或const ls&
}
ls& test2()
{
	ls s(1);
	s.func();//1
	return s;
}
ls test3()
{
	ls s(2);
	s.func();
	return s;
}
void test5()
{
	vector<int> v{ 1 };
	//auto& i = v.begin();//报错，不能使用非const引用接收右值
	const auto& i2 = v.begin();//可以使用const引用接收迭代器
	cout << *i2 << endl;
}


//关于为什么不将begin等迭代器函数的返回值设置为const iterator：
//因为使用迭代器时大多情况都需要++--迭代器，
//也就是说迭代器本身是需要修改的，虽然可以使用非const变量接收const变量也可以进行迭代器的移动，
//但这样做需要隐式类型转换，不符合迭代器本身的语义，因此，最好直接将迭代器函数的返回值设置为非const修饰
//这里测试一下const类型变量与非const类型之间的接收情况：
void test6()
{
	int a = 1;
	const int ca = a;
	int b = ca;
}//可以发现，const变量可以接收非const，非const也可以接收const，事实上，
 //此接受行为本质是右边隐式类型转换为常量类型，左侧接收了一个常量，类似int a = 1的行为，因此合法

//经过实验6，会产生疑问，那么什么时候const与非const之间不能相互接收呢？
//最常见的情况就是&，*这两类类型，涉及到之前提到过的权限放大与权限缩小问题（注：权限可以缩小，但不能放大）
void test7()
{
	int a = 1;
	const int& ca = a;//缩小权限，允许
	//int& b = ca;//放大权限，不允许

	int* pa = &a;
	const int* pa2 = pa;//缩小权限，允许
	//int* pa3 = pa2;//放大权限，不允许
}
//那么，对于容器中单个元素类型的const修饰问题呢？
void test8()
{
	vector<int> s{ 1,2,3,4 };
	//vector<const int> s2 = s;//错误，不存在对应的隐式类型转换，会导致错误

	//指针也不行：
	//vector<const int> ps = &s;//错误，无法隐式类型转换
}

//迭代器之间默认浅拷贝，但范围for（auto e : s情况下）却是深拷贝
void test9()
{
	vector<int> s{ 1,2,3,4 };
	for (const auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	auto i1 = s.begin();
	*i1 = 2;//迭代器本身就是以类指针形式产生的，其拷贝构造是浅拷贝，因此改变*i1就会改变s中的元素值
	//但混淆点在于，范围for的容器是深拷贝，也就是说auto e : s不会造成原容器s中元素的改变
	for (const auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	//auto
}

//cbegin迭代器指的是不能修改其中的元素值，而非不能更改容器结构
void test10()
{
	std::list<int> lst = { 1, 2, 3 };
	auto cit = lst.cbegin();  // const_iterator
	lst.insert(cit, 0);       // 合法：插入操作由容器执行，cit仅提供位置
	//lst.erase(cit);           // 合法：同上
	for (const auto& e : lst)
	{
		cout << e << " ";
	}
	//内部关键操作：更新原节点的指针：cit._node->prev->next = 新节点, cit._node->prev = 新节点。
	//可以更改的原因：cit只限制了_node不能更改，但是没有限制_node->prev（或者说，node节点本身是允许更改其内部成员的）
}//注：在 C++ 中，​​const容器的迭代器​​（如 begin()返回的迭代器）与​​非 const容器的 cbegin()迭代器​​的类型是相同的，均为 const_iterator。
 //但是，虽然类型相同，并且可以非const容器的cbegin修改容器结构，但由于const容器本身就不允许修改结构，导致其无法通过const_iterator进行修改，
 //也就是说，const_iterator虽然具备修改结构的能力，但具体能否修改结构还要看容器本身是否被const修改
 //*详细梳理：const_iterator通过const修饰*/->函数重载的返回值来限制容器中元素的修改，也就是限制val的修改，但如果能够直接访问const_iterator的成员变量_node就能够修改_node类中的指针变量*
 //注：在源码中，常常使用内部类/友元的方式书写迭代器，这才实现了上述的const_iterator配合insert修改容器结构


int main()
{
	//ls& s = test1();//当s类型为ls&时报错，返回值为临时变量类型，不能直接用引用类型接收

	ls& s = test2();
	s.func();//32759->就算使用&类型接收了test2中的局部变量，但实际该变量出test2函数时就已经被销毁，
	//因此其中数据甚至空间已不在属于可控范围
//ls& s2 = test3();//报错，test3的返回值为常量，不能使用&类型接收

	const ls& s3 = test3();//常量需使用const引用接收
	s3.func();//2

	//test5();
	//test6();
	//test7();
	//test8();
	//test9();
	test10();
	return 0;
}